﻿using System;
using System.Diagnostics;
using System.Linq;
using Zeta.Common;
using Zeta.Game;
using Zeta.Game.Internals.Actors;
using Zeta.Game.Internals.SNO;
using Zeta.TreeSharp;

namespace KadalaSpree.XCore
{
    public class XActor : XObject
    {
        public override XObjectType XObjectType { get { return XObjectType.XActor; } }
        public override Type GetType { get { return typeof(XActor); } }

        #region Fields

        internal ActorName _actorName;
        internal Vector3 _position;
        internal DateTime LastInteractAttempt = DateTime.MinValue;

        #endregion

        #region Delegates

        public delegate bool ActorConditionDelegate(XActor actor);

        #endregion

        #region Constructors

        public XActor()
        {
        }

        public XActor(int actorSNO)
        {
            _actorId = actorSNO;
            Update();
        }

        public XActor(ActorName actorName)
        {
            _actorName = actorName;
            Update();
        }

        public XActor(DiaObject diaObject)
        {
            _diaObject = diaObject;
            Update();
        }

        #endregion

        #region Properties

        internal int _actorId;

        internal DiaObject _diaObject;

        public int ActorId
        {
            get { return (_actorId > 0) ? _actorId : XLookup.Actors.ActorNameAndId.FirstOrDefault(row => row.Key == ActorName).Value; }
        }

        public virtual DiaObject DiaObject
        {
            get
            {
                if (!IsValid) Update();
                return _diaObject;
            }
        }

        public ActorName ActorName
        {
            get
            {
                if (_actorName == ActorName.None && _actorId != 0)
                {
                    _actorName = XLookup.Actors.ActorNameAndId.FirstOrDefault(a => a.Value == _actorId).Key;
                }
                return _actorName;
            }
        }

        public virtual bool IsValid
        {
            get { return _diaObject != null && _diaObject.IsValid &&
                _position.X != 0 && _position.Y != 0;
            }
        }

        public virtual bool Exists
        {
            get
            {
                if (!IsValid) { Update(); }
                return IsValid;
            }
        }

        public float Distance
        {
            get { return DiaObject.Distance; }
        }

        #region Shortcuts

        public Vector3 Position
        {
            get { return DiaObject.Position; }
        }

        #endregion

        public void Reset()
        {
            _actorId = 0;
            _diaObject = null;
            _position = new Vector3();
            _actorName = ActorName.None;
        }

        #endregion

        #region Methods

        internal virtual bool Update()
        {
            var actor = ZetaDia.Actors.GetActorsOfType<DiaObject>(true).FirstOrDefault<DiaObject>(a => a.ActorSNO == ActorId);

            X.Logger.Instance.Log("Search for {0} Id={1} {2}", ActorName, ActorId, (actor != null && actor.IsValid));

            if (actor != null && actor.IsValid)
            {
                _diaObject = actor;
                _actorId = actor.ActorSNO;
                _position = actor.Position;
                _actorName = ActorName;
                return true;
            }

            return false;
        }

        #endregion

        /// <summary>
        ///  Interacts with this actor
        /// </summary>
        public virtual void Interact()
        {
            if (!IsValid || ZetaDia.Me.LoopingAnimationEndTime > 0) 
                return;

            if (!IsValid || DateTime.UtcNow.Subtract(LastInteractAttempt).TotalMilliseconds < X.Settings.InteractionDelay) 
                return;

            X.Logger.Instance.Log("Interacting with {0} ActorType={0}", ActorName, DiaObject.CommonData.ActorType);    

            switch (DiaObject.ActorType)
            {
                case ActorType.Gizmo:
                    DiaObject.Interact();
                    break;
                default:
                    DiaObject.Interact();
                    break;
            }

            LastInteractAttempt = DateTime.UtcNow;
        }

        /// <summary>
        /// Interacts with this actor until the condition is satisfied
        /// <para>Returns Failure when condition is met</para>
        /// </summary>
        public RunStatus InteractUntil(ActorConditionDelegate condition)
        {
            if (condition(this) || !X.Player.IsValid)
                 return RunStatus.Failure;            

            Interact();

            return RunStatus.Running;
        }

        /// <summary>
        /// If this actor is within interact range
        /// </summary>
        public bool WithinInteractRange
        {
            get { return Exists && Distance <= X.Settings.InteractRange; }
        }

        /// <summary>
        /// Move to this actor
        /// </summary>
        public RunStatus MoveTo()
        {
            if (!IsValid) 
                return RunStatus.Failure;

            return X.Movement.Move(ActorName);
        }

        /// <summary>
        /// Moves towards this actor until condition is met
        /// <para>Returns Failure when condition is met</para>
        /// </summary>
        public RunStatus MoveTowardsUntil(ActorConditionDelegate condition)
        {
            if (condition(this) || !X.Player.IsValid || !IsValid)
                return RunStatus.Failure;

            return MoveTo();
        }
        

    }
}